package com.chuangjiangx.member.stored.ddd.domain.service;

import com.chuangjiangx.member.coupon.ddd.domain.model.MbrHasCoupon;
import com.chuangjiangx.member.basic.ddd.domain.model.MbrAccount;
import com.chuangjiangx.member.basic.ddd.domain.model.MbrAccountRepository;
import com.chuangjiangx.member.basic.ddd.domain.model.Member;
import com.chuangjiangx.member.basic.ddd.domain.model.MemberRepository;
import com.chuangjiangx.member.basic.ddd.domain.service.MemberMsgDomainService;
import com.chuangjiangx.member.basic.ddd.domain.service.MemberRedisDomainService;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrRechargeRule;
import com.chuangjiangx.member.stored.ddd.domain.model.RechargeRuleRepository;
import com.chuangjiangx.member.basic.ddd.domain.model.MemberId;
import com.chuangjiangx.member.basic.ddd.domain.model.OperatorInfo;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrStoredType;
import com.chuangjiangx.member.score.ddd.domain.model.MbrScoreType;
import com.chuangjiangx.member.coupon.ddd.domain.service.CouponDomainService;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrOrder;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrOrderRepository;
import com.chuangjiangx.member.score.ddd.domain.model.MbrScoreStream;
import com.chuangjiangx.member.score.ddd.domain.repository.MbrScoreStreamRepository;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrStoredStream;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrStoredStreamRepository;
import com.chuangjiangx.member.stored.ddd.domain.event.MbrPaySuccessEvent;
import com.chuangjiangx.member.stored.ddd.domain.exception.MemberPaymentException;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrOrderId;
import com.chuangjiangx.member.stored.ddd.domain.model.MbrOrderStatus;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @author cj-ch
 * @date 2018/5/24 上午8:48
 */
@Slf4j(topic = "pay")
@Service
public class MbrRechargeService {
    /**
     * 充值锁前缀
     */
    private static final String RECHARGE_LOCK_PRE = "R_L_";

    @Autowired
    private MemberRedisDomainService memberRedisDomainService;
    @Autowired
    private MbrScoreStreamRepository mbrScoreStreamRepository;
    @Autowired
    private MbrStoredStreamRepository mbrStoredStreamRepository;
    @Autowired
    private MbrAccountRepository mbrAccountRepository;
    @Autowired
    private MemberRepository memberRepository;
    @Autowired
    private MbrOrderRepository mbrOrderRepository;
    @Autowired
    private MemberMsgDomainService memberMsgDomainService;
    @Autowired
    private RechargeRuleRepository rechargeRuleRepository;
    @Autowired
    private CouponDomainService couponDomainService;
    /**
     * 处理充值业务
     * @param event
     */
    @Transactional(rollbackFor = Exception.class)
    public void service(MbrPaySuccessEvent event){
        if(null == event || !check(event)){
            return;
        }

        MbrPaySuccessEvent.Type serviceType = event.serviceType();
        final String lockKey = RECHARGE_LOCK_PRE + event.getOrderPayNumber() + serviceType;
        //获取锁
        if(!memberRedisDomainService.lock(lockKey, String.valueOf(System.currentTimeMillis()),5,TimeUnit.MINUTES)){
            log.info("处理充值业务获取锁失败:{}",event.toString());
            return;
        }
        Member member = memberRepository.fromId(new MemberId(event.getMemberId()));
        if(null == member){
            throw new MemberPaymentException("","会员不存在");
        }
        MbrAccount mbrAccount = mbrAccountRepository.fromMemberId(member.getId());
        if(null == member){
            throw new MemberPaymentException("","会员账户不存在");
        }
        MbrOrder mbrOrder = mbrOrderRepository.fromId(new MbrOrderId(event.getMbrOrderId()));

        //金额校验
        if(mbrOrder.getOrderAmount().compareTo(event.getAmount()) != 0){
            log.warn("会员充值金额校验不通过,充值金额:{},充值事件:{}",mbrOrder.getPaidAmount(),event.toString());
            throw new MemberPaymentException("","会员充值金额校验不通过");
        }

        mbrOrder.rechargeSuccess(
                event.getMerchantUserId(),
                event.getStoreId(),
                event.getStoreUserId(),
                event.getPayEntry(),
                event.getPayType(),
                event.getPayTerminal(),
                event.getOrderPayNumber()
        );

        //记录储值流水
        MbrStoredStream mbrStoredStream = recordMbrStoredStream(member, mbrAccount, mbrOrder, event);

        //充值赠送业务
        try {
            giftService(event, member, mbrAccount, mbrOrder, mbrStoredStream);
        } catch (Exception e) {
            e.printStackTrace();
        }

        //持久化订单
        mbrOrderRepository.update(mbrOrder);
        //保存账户数据
        mbrAccountRepository.update(mbrAccount);
        memberRedisDomainService.delKey(lockKey);
    }

    /**
     * 前置条件检查
     * @param event
     * @return
     */
    private boolean check(MbrPaySuccessEvent event){
        //充值业务,充值是预下单
        //查询会员订单表  - 根据会员订单Id
        MbrOrder mbrOrder = mbrOrderRepository.fromId(new MbrOrderId(event.getMbrOrderId()));
        if(null == mbrOrder){
            throw new MemberPaymentException("","会员订单不存在");
        }else{
            if(Objects.equals(MbrOrderStatus.PAID,mbrOrder.getStatus())){
                //充值订单已处理
                return false;
            }
        }
        return true;
    }

    /**
     * 充值赠送业务
     * @param event
     * @param member
     * @param mbrAccount
     * @param mbrOrder
     * @param mbrStoredStream
     */
    private void giftService(MbrPaySuccessEvent event, Member member, MbrAccount mbrAccount, MbrOrder mbrOrder, MbrStoredStream mbrStoredStream) {
        //获取充值规则
        if(-1 == mbrOrder.getMbrRechargeRuleId().getId()){
            //自定义储值
            return;
        }
        MbrRechargeRule mbrRechargeRule = rechargeRuleRepository.fromId(mbrOrder.getMbrRechargeRuleId());

        switch (mbrRechargeRule.getMbrGiftType()){
            case SCROE:
                //赠送积分
                if(mbrRechargeRule.getGiftScore() < 1){
                    break;
                }
                //记录积分流水
                recordMbrScoreStream(event, member, mbrAccount, mbrStoredStream, mbrRechargeRule);
                break;
            case AMOUNT:
                //z赠送金额
                if(mbrRechargeRule.getGiftAmount().compareTo(BigDecimal.ZERO) <= 0){
                    break;
                }
                //记录储值流水
                recordMbrStoredStream(member,mbrAccount,mbrRechargeRule,mbrStoredStream, event);
                break;
            case COUPON:
                //赠送卡券
                //
                MbrHasCoupon mbrHasCoupon = couponDomainService.memberClaimCoupon(member.getId(), mbrRechargeRule.getCouponNumber());
                mbrOrder.rechargeSuccessGiftCoupon(mbrHasCoupon.getId());
                break;
            case CUSTOMIZE:
                break;
            default:
                break;
        }
    }

    /**
     * 记录积分流水
     * 账户积分变动,新增积分流水,发送消息
     */
    private void recordMbrScoreStream(MbrPaySuccessEvent event, Member member, MbrAccount mbrAccount, MbrStoredStream mbrStoredStream, MbrRechargeRule mbrRechargeRule) {
        Long score = mbrRechargeRule.getGiftScore();
        if(0 >= score){
            return;
        }
        OperatorInfo operatorInfo = event.getOperatorInfo();
        mbrAccount.changeScore(score,MbrScoreType.GIFT);
        String remark = "充"+mbrRechargeRule.getAmount()+"元送"+score+"积分";
        MbrScoreStream mbrScoreStream = new MbrScoreStream(
                member.getId(),
                mbrRechargeRule.getGiftScore(),
                MbrScoreType.GIFT,
                mbrStoredStream.getId(),
                null,
                null,
                null,
                mbrAccount.getAvailableScore(),
                remark,
                event.getPayEntry(),
                operatorInfo.getMerchantUserId(),
                operatorInfo.getStoreUserId(),
                operatorInfo.getStoreId()
        );
        mbrScoreStreamRepository.save(mbrScoreStream);
        //消息推送
        memberMsgDomainService.sendMbrScoreChangeMsg(member.getOperationInfo().getMerchantId().getId(), mbrAccount, mbrScoreStream,
                remark);
    }

    /**
     * 记录储值流水
     * 账户储值变动,新增储值流水,发送消息
     */
    private MbrStoredStream recordMbrStoredStream(Member member, MbrAccount mbrAccount, MbrOrder mbrOrder, MbrPaySuccessEvent event) {

        OperatorInfo operatorInfo = event.getOperatorInfo();
        //账户变动
        mbrAccount.changeMoney(mbrOrder.getPaidAmount(),MbrStoredType.RECHARGE);
        //新增储值流水记录
        MbrStoredStream mbrStoredStream = new MbrStoredStream(
                member.getId(),
                mbrOrder.getPaidAmount(),
                mbrAccount.getAvailableBalance(),
                MbrStoredType.RECHARGE,
                null,
                "充值",
                mbrOrder.getId(),
                null,
                operatorInfo.getMerchantUserId(),
                operatorInfo.getStoreUserId(),
                operatorInfo.getStoreId()
        );
        mbrStoredStreamRepository.save(mbrStoredStream);
        if(mbrOrder.getPaidAmount().compareTo(BigDecimal.ZERO) > 0){
            //消息推送
            memberMsgDomainService.sendMbrCardRechargeMsg(member, mbrAccount, mbrStoredStream,
                    () -> operatorInfo.getOperatorAddress());
        }
        return mbrStoredStream;
    }

    /**
     * 充值赠送的储值流水
     * @param member
     * @param mbrAccount
     * @param rule
     * @param parent
     * @param event
     * @return
     */
    private MbrStoredStream recordMbrStoredStream(Member member, MbrAccount mbrAccount, MbrRechargeRule rule,MbrStoredStream parent, MbrPaySuccessEvent event) {
        BigDecimal giftAmount = rule.getGiftAmount();
        if(giftAmount.compareTo(BigDecimal.ZERO) <= 0){
            return null;
        }
        OperatorInfo operatorInfo = event.getOperatorInfo();
        //账户变动
        mbrAccount.changeMoney(giftAmount,MbrStoredType.RECHARGE_GIFT);
        String remark = "充" + parent.getAmount() + "元送"+giftAmount+"元" ;
        //新增储值流水记录
        MbrStoredStream mbrStoredStream = new MbrStoredStream(
                member.getId(),
                giftAmount,
                mbrAccount.getAvailableBalance(),
                MbrStoredType.RECHARGE_GIFT,
                parent.getId().getId(),
                remark,
                parent.getMbrOrderId(),
                null,
                operatorInfo.getMerchantUserId(),
                operatorInfo.getStoreUserId(),
                operatorInfo.getStoreId()
        );
        mbrStoredStreamRepository.save(mbrStoredStream);
        //消息推送
        memberMsgDomainService.sendMbrCardRechargeMsg(member, mbrAccount, mbrStoredStream,
                () -> operatorInfo.getOperatorAddress());
        return mbrStoredStream;
    }

}
